package jxtcpclient

import (
	"bufio"
	"context"
	"encoding/binary"
	"errors"
	"net"
	"sync"
	"time"

	"github.com/google/uuid"
)

type Conn struct {
	Id         string
	Address    string
	conn       net.Conn
	onMessage  func(*Message)
	onClose    func(error)
	MashineId  uint32
	clocker    sync.Mutex
	sendlocker sync.Mutex
	writer     *bufio.Writer
	reader     *bufio.Reader
	State      int32
	ctx        context.Context
	cancel     context.CancelFunc
}

func (c *Conn) Init() {
	ctx, cancel := context.WithCancel(context.Background())
	c.cancel = cancel
	c.ctx = ctx
	c.Relink()
	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			default:
				if c.conn == nil {
					c.Relink()
				}
				for {
					m, err := ReadMessage(c.reader)
					if err != nil {
						c.Close(errors.New("读取消息错误:" + err.Error()))
						break
					}
					if c.onMessage != nil {
						c.onMessage(m)
					}
				}
			}
		}
	}()
}
func (c *Conn) Stop() {
	c.cancel()
	c.conn.Close()
}
func (c *Conn) Relink() {
	c.clocker.Lock()
	defer c.clocker.Unlock()
	for {
		conn, err := net.Dial("tcp", c.Address)
		if err == nil {
			c.conn = conn
			c.writer = bufio.NewWriter(conn)
			c.reader = bufio.NewReader(conn)
			//写入客户端id
			err := binary.Write(c.writer, binary.BigEndian, c.MashineId)
			if err != nil {
				continue
			}
			_, err = c.writer.Write([]byte(c.Id))
			if err != nil {
				continue
			}
			err = c.writer.Flush()
			if err != nil {
				continue
			}
			return
		} else {
			clientlog.Error("创建TCP客户端链接错误", err.Error())
		}
		time.Sleep(time.Microsecond * 500)
	}
}

func (c *Conn) SendMessage(m *Message) error {
	if !c.sendlocker.TryLock() {
		return errors.New("当前链接未准备就绪")
	}
	defer func() {
		c.sendlocker.Unlock()
	}()
	err := m.WriteMessage(c.writer)
	if err != nil {
		c.Close(errors.New("写入数据错误：" + err.Error()))
	}
	return err
}
func (c *Conn) Close(err error) {
	c.clocker.Lock()
	defer c.clocker.Unlock()
	if c.onClose != nil {
		c.onClose(err)
	}
	if c.conn != nil {
		c.conn.Close()
	}
	c.writer = nil
	c.reader = nil
	c.conn = nil
}
func NewConn(address string, mashineid uint32, onmessage func(*Message), onclose func(error)) *Conn {
	outData := &Conn{
		Id:        uuid.NewString(),
		Address:   address,
		MashineId: mashineid,
		onMessage: onmessage,
		onClose:   onclose,
	}
	outData.Init()
	return outData
}
